home *** CD-ROM | disk | FTP | other *** search
- 6 TRACKDISK DEVICE
-
- 6.1 INTRODUCTION
-
- All Amiga models are delivered with at least one internal
- disk drive, but you may connect up to three extra disk drives.
- The disk drivers are using normal double sided double density
- (2DD) 3 1/2" disks. 5 1/4" disks may also be used, but this is
- not supported by the trackdisk device.
-
- The trackdisk device consists of a set of low level routines
- which are used by other higher level processes like AmigaDOS.
- When you want to read and write data you should normally only
- use AmigaDOS or the special file commands in C. The trackdisk
- device should only be used when you want to write disk
- viewers/editors, or disk-copy programs.
-
- Although the disk routines are normally considered to be very
- slow on the Amiga, it should be noted that it is actually not
- the disk drivers which are slow. AmigaDOS is using a very
- flexible disk operating system and because of this the disk
- drivers appears to be very slow. The low level routines are
- on the other hand very quick and are turbocharged with the
- "Blitter" (special coprocessor on the Amiga) which is
- specialized in moving large quantities of data extremely fast.
-
- To read and write so called "raw data" with the lowlevel
- routines is much faster that to use the higher and more
- sophisticated AmigaDOS routines. Many games use these low
- level routines to quickly load graphics and sound effects.
-
- Normal programs should, as said before, not use the lowlevel
- routines described here. However, it can still be interesting
- to read about it anyway, since it is always good to know what
- is actually happening inside the Amiga.
-
-
-
- 6.2 AMIGA DISK DRIVERS
-
- The trackdisk device was designed to handle normal double sided
- double density (2DD) 3 1/2" disks (also called "1 megabyte
- disks"). The disk can be logically (fysically too, but that is
- not recommended) be divided into several small data areas where
- the actual data is stored.
-
- Each data area, normally called "Sector", can store 512 bytes
- of data and 16 bytes of so called "label" data. The label area
- is used to identify the sector and what is stored here, and can
- therefore not be used to store raw data.
-
- A group of eleven sectors is called "Cylinder" or "Track", and
- there exist 80 cylinders on each side of the disk. In total you
- can store 512 (bytes/sector) * 11 (sectors/cylinder) * 80
- (cylinders/side) * 2 (double sided disks) = 901120 bytes = 880KB
- (901120 / 1024).
-
- The trackdisk device will only handle whole cylinders. Even if
- you only want to read some sectors of a cylinder the complete
- cylinder will be loaded into the trackdisk's own memory buffer.
- This technique of reading and writing complete cylinders
- greatly improves the storage capacity and speed of the drives.
- The actual reading and writing process is very quick compared
- to the time it takes for the drive to move the head to the right
- position and start rotating the disk.
-
- Your program will not notice that the trackdisk device is only
- using complete cylinders. If you want to read some sectors, the
- trackdisk device will read a complete cylinder, but gives you
- only those sectors you wanted. If you later want to read some
- sectors more it may happen that the device immediately can give
- you the data you wanted without having to access the disk.
-
-
-
- 6.3 TRACKDISK DEVICE
-
- The trackdisk device is controlled in the same manner as all
- other devices. You send your commands to it with help of a
- request block, and the device will send messages back to a
- specific reply port. So, to use the trackdisk device you have
- to: (Same as with most other devices.)
-
- 1. Create a message port with which the device can send
- messages back to us. See chapter 1 "Devices" for more
- information.
-
- 2. Allocate and preinitialize a request block.
-
- 3. Open the trackdisk device.
-
-
-
- 6.3.1 REQUESTBLOCK
-
- A normal IOStdReq structure ("standard request block") is in
- most cases possible to use, but if you want to use some special
- features described further down you have to use the extended
- request block "IOExtTD". This IOExtTD structure is declared in
- header file "devices/trackdisk.h" as:
-
- struct IOExtTD
- {
- struct IOStdReq iotd_Req;
- ULONG iotd_Count;
- ULONG iotd_SecLabel;
- };
-
-
- iotd_Req: The top part of the request block consists of
- a standard request block (IOStdReq structure).
-
- iotd_Count: Each disk drive you are working with is assigned
- a counter value which is increased each time a
- disk is removed. If you are using the "extended"
- commands (described below) the trackdisk device
- will check the driver's count value with this
- value. If the counter is greater than this value
- the request will be aborted.
-
- If you are going to working with a disk it is
- best to first get the driver's current count
- value and store it here. You will then be sure
- that the user will not change disks without you
- knowing about it.
-
- iotd_SecLabel: As described above, each sector has a 16 bytes
- long "label" are. This label area is not used by
- the track disk device. However, if you want to
- read and/or write to this label area you should
- use the "extended" read and write commands
- described below, and this field should contain
- a pointer to some memory where the label data
- can be stored/read from.
-
- NOTE! If you intend to read some sectors' label
- areas you must make sure you have allocated a
- buffer that is large enough. 16 bytes is needed
- for every sector you intend to read. If you want
- to write new sector labels the memory must of
- course also be large enough, but also
- initialized with the values you want to write.
-
-
- Since this is an extended request block you have to use the
- CreateExtIO() function rather than CreateStdIO().
-
- Synopsis: ext_req = CreateExtIO( msg_port, size );
-
- ext_req: (struct IORequest *) Pointer to the new extended
- request block, or NULL if the request block could
- not be created.
-
- msg_port: (struct MsgPort *) Pointer to the message port
- the device should use to communicate with you.
-
- size: (long) The number of bytes that should be allocated
- for the extended request block. Use the function
- sizeof() to find the exact number of bytes needed.
-
-
- If you are not going to use the "extended" commands which are
- described below, but only the normal commands, you do not need
- to use this extended request block. A normal standard request
- block will be enough. However, if you later would change your
- program so it starts to use the extended commands you have to
- remember to also change the type of request block. Usually it
- is best to always use an extended request block, although it
- sometimes it not strictly necessary.
-
-
-
- 6.3.2 OPEN THE TRACKDISK DEVICE
-
- Once you have opened a (reply) message port and created the
- request block you can open the trackdisk device. This is done
- with help of the now for us famous OpenDevice() function.
-
- Synopsis: error = OpenDevice( name, unit, req, flags );
-
- error: (long) If OpenDevice() managed to open the device
- it returns 0, else an error number is returned.
-
- name: (char *) Name of the device you want to open.
- The name of the trackdisk device is (surprise)
- "trackdisk.device". This name has been defined
- is the header file as "TD_NAME".
-
- When you are opening devices you should always try
- to use the defined names rather than the actual
- names. If Commodore would some day change the name
- of the device (very unlikely though) you only have
- to recompile your program with the new header
- files, and your program will work again.
-
- unit: (long) Which disk drive you want to use. All Amigas
- are sold with at leas one internal drive (df0:),
- but many have bought at lest one more drive. In
- total there may exist up to four disk drivers.
- Drive df0: has been given the unit number 0, df1:
- the number 1, and so on...
-
- If you are trying to access a drive which does
- not exist, OpenDevice() will fail. See Example 2.
-
- req: (struct IORequest *) Pointer to the request block
- you have created and initialized.
-
- flags: (long) This field is ignored by the trackdisk
- device.
-
-
-
- 6.3.3 CLEAN UP
-
- As always, do NOT forget to clean up after yourself! All opened
- message ports, request blocks, devices etc. must be closed and
- removed before your program may terminate. Here is what you
- have to do:
-
- 1. Make sure that all your requests have been completed or
- aborted.
-
- 2. Close the trackdisk device with help of the CloseDevice()
- function.
-
- Synopsis: CloseDevice( req );
-
- reg: (struct IORequest *) Pointer to the device's
- request block.
-
- 3. Delete all request blocks you have created. Note that if
- you have allocated extended request blocks you must use
- the DeleteExtIO() function, but if you have allocated
- standard request blocks you can use the DeleteStdIO()
- function. As recommended above you should only use the
- extended request blocks with the trackdisk device.
-
- Synopsis: DeleteExtIO( std_req, size );
-
- std_req: (struct IOStdReq *) Pointer to the extended
- request block you want to delete.
-
- size: (long) The size of the request block, in bytes.
-
- 4. Close all message ports you have opened. Do this by
- calling the DeletePort() function. Note that all messages
- still left at the port must be removed before you may
- close the message port.
-
- Synopsis: DeletePort( msg_port );
-
- msg_port: (struct MsgPort *) Pointer to the MsgPort
- structure that should be deallocated.
-
-
-
- 6.4 COMMANDS
-
- When the trackdisk device has successfully been opened you may
- start to send requests to it. The trackdisk device can do a lot
- of useful stuff, so there exist several commands that could be
- used. These commands can be divided into two groups. The first
- group consists of the "normal" commands. These commands will
- not check if the disk has been removed or changed. Note also
- that these commands can not be used to read or write the label
- areas of the sectors.
-
- The second group consists of the "extended" commands - those
- commands that needs the extended IOExtTD structure. These
- commands will check the unit counter too see if the disk has
- been changed, and if so, the commands will automatically be
- aborted. These commands can be used if you want to access
- the sectors' label areas.
-
- When you want to read and write data you have to give the
- "io_Data" field of the request block a pointer to some memory
- where all data you read will be placed, or if you are writing,
- where the data that should be written is stored. This buffer
- should be "word aligned" which means that it must start on
- an even byte address. The memory must also be of the type
- "Chip Memory" since the blitter will be used to speed up the
- operations. To fulfill both demands you should allocate the
- memory with help of the AllocMem function. See examples further
- down for more information.
-
- The "io_Offset" value contains the offset value from which
- the trackdisk device will start to read/write from. The
- correct offset value is calculated with this formula:
-
- TD_SECTOR*( NUMSECS * 2 * cylinder + NUMSECS * head + sector )
-
- The words in capital letters are defined in the header file
- "devices/trackdisk.h" as:
- TD_SECTOR 512 (512 bytes / sector)
- NUMSECS 11 (11 sectors / cylinder)
-
- For example, if you want to start to read at side 0, cylinder 5,
- and sector 3, you should set the offset value to:
-
- TD_SECTOR * ( NUMSECS * 2 * 5 + NUMSECS * 0 + 3 );
-
-
- You tell the trackdisk device to read/write a specific number
- of bytes by setting the "io_Length" field to the number of
- bytes that should be transferred. Note that you must read
- complete sectors, and the length should therefore be set to:
- TD_SECTOR * "nr of desired sectors". (The trackdisk device
- will only read complete cylinders, but has nothing to do with
- this restriction of only reading complete sectors.)
-
- The "io_Actual" filed will contain the number of actual bytes
- written/read. If this value is not the same as the "io_Length"
- something has happened. Check the "io_Error" field to find
- any error values. See below for more inforamtion about error
- messages.
-
-
-
- 6.4.1 READ
-
- Probably one of the most commonly used commands is undoubtedly
- the read command. If you simply want to read some data, and
- you do not care if the user has changed disks, you should use
- the "CMD_READ" command. However, most times it is best to check
- if the disk has been changed, before you start to read, and
- then you should use the extended "ETD_READ" command.
-
- Here is an example: ("exreq" is a pointer to the request block,
- "buffer" is a pointer to some memory which is word aligned and
- is of the type chip memory. The buffer must at least be 2 * 512
- bytes.)
-
- /* We want to read: */
- exreq->iotd_Req.io_Command = ETD_READ;
-
- /* Pointer to our buffer: */
- exreq->iotd_Req.io_Data = (APTR) buffer;
-
- /* Read two sectors: */
- exreq->iotd_Req.io_Length = TD_SECTORS * 2;
-
- /* Start to read at side 0, cylinder 5, and sector 3: */
- exreq->iotd_Req.io_Offset = (LONG)
- TD_SECTOR*( NUMSECS * 2 * 5 + NUMSECS * 0 + 3 );
-
- /* Do our request: */
- DoIO( exreq );
-
-
-
- 6.4.2 WRITE
-
- Same as with reading, there exist two commands to write data.
- The shorter "CMD_WRITE" will simply write to the disk which is
- currently in the drive. The extended "ETD_WRITE" will check
- that the disk has not been changed.
-
- Here is a similar example as the one above. This time we will
- try to write two sectors of data:
-
-
- /* We want to write: */
- exreq->iotd_Req.io_Command = ETD_WRITE;
-
- /* Pointer to our buffer: */
- exreq->iotd_Req.io_Data = (APTR) buffer;
-
- /* Write two sectors: */
- exreq->iotd_Req.io_Length = TD_SECTORS * 2;
-
- /* Start to write at side 0, cylinder 5, and sector 3: */
- exreq->iotd_Req.io_Offset = (LONG)
- TD_SECTOR*( NUMSECS * 2 * 5 + NUMSECS * 0 + 3 );
-
- /* Do our request: */
- DoIO( exreq );
-
-
-
- 6.4.3 MOTOR ON/OFF
-
- You can tell the trackdisk device to start or stop the motor of
- the disk drive. Although you do not need to start the motor
- (this will be done automatically if you start to read or write),
- you must make sure to stop the drive after you have used it.
- You can either use the command "TD_MOTOR" or "ETD_MOTOR". The
- later will check that the disk has not been changed.
-
- If the "io_Length" field is set to 0 the drive will be turned
- off. If the field is set to 1 the drive will be turned on. Here
- is an example ("exreq" is a pointer to the request block):
-
- /* Turn motor on/off: */
- exreq->iotd_Req.io_Command = ETD_MOTOR;
-
- /* Turn the motor on: (0 = off, 1 = on) */
- exreq->iotd_Req.io_Length = 1;
-
- /* Do our request: */
- DoIO( exreq );
-
-
- Remember to always turn off the motor after you have used the
- drive! (If the motor already was on before you started to use
- it you do not need to turn it off. This means that some other
- device is also using the drive.)
-
-
-
- 6.4.4 UPDATE THE DISK
-
- When you write data to the disk it will not immediately be
- stored. The trackdisk device will as explained above only read
- and write complete cylinders, and if you do not write exactly
- an even number of cylinders, some data will be temporarily
- stored in the trackdisk device's internal memory buffer. When
- you later change cylinder the buffer will be copied out before
- the head is moved.
-
- You can tell the trackdisk device to immediately copy the
- data in the buffer to the disk by sending a "CMD_UPDATE" or
- "ETD_UPDATE" command. The later command will as usual check if
- the disk has been removed or changed before the buffer is
- copied onto the disk.
-
- After you have written something to the disk and before you
- turn the motor off you should use this command. You will then
- be sure that all data you have written will actually be on the
- disk.
-
- Here is an example:
-
- /* Update the disk (move any data still left in the buffer */
- /* to the disk: */
- exreq->iotd_Req.io_Command = ETD_UPDATE;
-
- /* Do our request: */
- DoIO( exreq );
-
-
-
- 6.4.5 CLEAR BUFFER
-
- You can tell the trackdisk device to clear it's internal
- buffer by sending a "CMD_CLEAR" or "ETD_CLEAR" command. Be
- sure that you do not clear anything that still has not been
- moved out onto the disk.
-
- This clear command is recommended to use after you have noticed
- that the disks have been changed. This will ensure that old
- data from another disk will not be written to the new disk.
-
- Here is an example:
-
- /* Clear the temporary buffer: */
- exreq->iotd_Req.io_Command = ETD_CLEAR;
-
- /* Do our request: */
- DoIO( exreq );
-
-
-
- 6.4.6 POSITION THE HEAD
-
- You can move the head to a specific position by sending a
- "TD_SEEK" or "ETD_SEEK" command. The head will simply be moved
- to the specified position in the "io_Offset" field, but no
- data will be read or written. This command is usually not
- needed since the head will always be placed on the correct
- position when you read or write data.
-
- Here is an example:
-
- /* Position the head: */
- exreq->iotd_Req.io_Command = ETD_SEEK;
-
- /* Move to position: side 1, cylinder 2, and sector 4: */
- exreq->iotd_Req.io_Offset = (LONG)
- TD_SECTOR*( NUMSECS * 2 * 2 + NUMSECS * 1 + 4 );
-
- /* Do our request: */
- DoIO( exreq );
-
-
-
- 6.4.7 FORMAT
-
- With help of the "TD_FORMAT" or "ETD_FORMAT" command you can
- format a cylinder. You give the "io_Data" field a pointer to
- some data which will be written onto the new cylinder, and
- set the "io_Length" to NUMSECS * TD_SECTOR (one cylinder). The
- "io_Offset" field should be set to an offset value to the
- cylinder you want to format.
-
- If you write data to a cylinder and you receive a hard write
- error (the cylinder is not formatted), you can try to reformat
- that cylinder. Note that it is usually best to tell the user
- to use some other disk rather than trying to correct this one.
- The user can then try to use "Diskdoctor" or similar programs
- to solve the disk problem.
-
- Here is an example:
-
- /* We want to format one cylinder: */
- exreq->iotd_Req.io_Command = ETD_FORMAT;
-
- /* Pointer to our buffer: (Must be at least one */
- /* cylinder of bytes large, NUMSECS * TD_SECTOR. */
- exreq->iotd_Req.io_Data = (APTR) buffer;
-
- /* Format one cylinder: */
- exreq->iotd_Req.io_Length = NUMSECS * TD_SECTOR;
-
- /* Format cylinder 5 on side 0: */
- exreq->iotd_Req.io_Offset = (LONG)
- TD_SECTOR*( NUMSECS * 2 * 5 + NUMSECS * 0 );
-
- /* Do our request: */
- DoIO( exreq );
-
-
-
- 6.4.8 REMOVE
-
- You can send a "TD_REMOVE" command to the trackdisk device,
- and it will then increase the driver's count value. It will
- look like that the user has changed disks.
-
- Here is an example:
-
- /* Change disks: */
- exreq->iotd_Req.io_Command = TD_REMOVE;
-
- /* Do our request: */
- DoIO( exreq );
-
-
-
- 6.4.9 GET THE DISK'S CURRENT COUNT NUMBER
-
- The "extended" commands check if the disk has changed or not
- before they are executed. If the disk has changed the command
- is aborted. The commands compare the "iotd_Count" value of the
- request block with the disk's current count value. If the
- disk's count value is larger than the "iotd_Count" the command
- is aborted and the error flag "TDERR_DiskChanged" is set.
-
- Before you use these extended commands you should therefore
- get the disk's current count value and store it in the request
- block. To get the disk's current count value you send an
- extended request block with the "TD_CHANGENUM" command set to
- the trackdisk device. The device will as soon as possible
- return your request with a copy of the current count value in
- the "io_Actual" field of the request block.
-
- Here is an example:
-
- /* Store the current count value here: */
- ULONG count_value;
-
- /* Get the disk's change count value: */
- extreq->iotd_Req.io_Command = TD_CHANGENUM;
-
- /* Do our request, and return when completed: */
- DoIO( extreq );
-
- /* Save the count value in the variable: */
- count_value = extreq->iotd_Req.io_Actual;
-
-
- After you have got the current count value you should give it
- the the request blocks which should later be used. The request
- block can then use the extended commands. Example:
-
- /* Give the request block the current count value: */
- extreg->iotd_Count = count_value;
-
-
- After your program has received a "TDERR_DiskChanged" you should
- try to get the new count value before you attempt to access the
- new disk.
-
-
-
- 6.4.10 CHECK IF THERE IS A DISK IN THE DRIVE OR NOT
-
- With help of the trackdisk device can you check if there is a
- disk in a drive or not. To do this you use the "TD_CHANGESTATE"
- command. If there is a disk in the drive will the "io_Actual"
- field of the request will contain zero. On the other hand, if
- there is not a disk in the drive the field will contain a non
- zero value.
-
- /* Check if there is a disk in the drive or not: */
- extreq->iotd_Req.io_Command = TD_CHANGESTATE;
-
- /* Do our request, and return when completed: */
- DoIO( extreq );
-
- /* Is there a disk in the drive? */
- if( extreq->iotd_Req.io_Actual )
- printf( "No disk in the drive!\n" );
- else
- printf( "There is a disk in the drive!\n" );
-
-
-
- 6.4.11 CHECK IF THE DISK IS WRITE PROTECTED OR NOT
-
- It can sometimes be very useful to know if the disk is write
- protected or not. With help of the "TD_PROTSTATUS" command we
- can easily check this. If the disk is write protected the
- "io_Actual" field will contain a non zero value. On the other
- hand, if the disk is not write protected the field will contain
- zero.
-
- /* Check if the disk is write protected or not: */
- extreq->iotd_Req.io_Command = TD_PROTSTATUS;
-
- /* Do our request, and return when completed: */
- DoIO( extreq );
-
- /* Is the disk write protected or not? */
- if( extreq->iotd_Req.io_Actual )
- printf( "The disk is write protected!\n" );
- else
- printf( "The disk is not write protected!\n" );
-
-
-
- 6.4.12 GET DRIVE TYPE
-
- The trackdisk device can also be used to tell you what type of
- disk drives are connected. If you send a request block with the
- "TD_GETDRIVETYPE" command set, the "io_Actual" field of the
- request block will either contain the flag "DRIVE3_5" or
- "DRIVE5_25" when it is returned. The "DRIVE3_5" means that it
- is a normal 3 1/2" disk drive, and the "DRIVE5_25" means that
- it is a 5 1/4 (IBM) disk drive.
-
- /* Check drive type: */
- extreq->iotd_Req.io_Command = TD_GETDRIVETYPE;
-
- /* Do our request, and return when completed: */
- DoIO( extreq );
-
- /* What type is it? */
- if( extreq->iotd_Req.io_Actual == DISK3_5 )
- printf( "It is a normal 3 1/2\" disk drive.\n" );
- else
- if( extreq->iotd_Req.io_Actual == DISK5_25 )
- printf( "It is a 5 1/4\" disk drive.\n" );
- else
- printf( "It is a very strange disk drive!\n" );
-
-
-
- 6.4.13 GET THE NUMBER OF TRACKS
-
- The "TD_GETNUMTRACKS" command is used to check how many tracks
- are used by the drive. When the request is returned you can
- check the "io_Actual" field of the request to get the number of
- tracks. A normal 3 1/2 disk drive uses 160 tracks (80 tracks /
- side).
-
- /* Get the number of tracks this drive is using: */
- extreq->iotd_Req.io_Command = TD_GETNUMTRACKS;
-
- /* Do our request, and return when completed: */
- DoIO( extreq );
-
- /* How many tracks? */
- printf( "No tracks: %d\n", extreq->iotd_Req.io_Actual );
-
-
-
- 6.5 ERRORS
-
- When you are using the trackdisk device you will most definitely
- receive many errors. The "io_Error" field of the requestblock
- will contain 0 if the request was successfully executed, but if
- something failed it will contain one of the following error
- numbers: (Defined in the header file "devices/trackdisk.h".)
-
- Error code Description
- -------------------------------------------------------------
- TDERR_NotSpecified A strange error has occurred.
- TDERR_NoSecHdr Could not find a sector.
- TDERR_BadSecPreamble Strange sector.
- TDERR_BadSecID Also a strange sector.
- TDERR_BadHdrSum Strange header.
- TDERR_BadSecSum Strange data areas.
- TDERR_TooFewSecs Missing some sectors.
- TDERR_BadSecHdr Yet another strange sector.
- TDERR_WriteProt The disk is write protected.
- TDERR_DiskChanged The disk has been removed/changed.
- TDERR_SeekError Could not reach track zero.
- TDERR_NoMem Not enough memory.
- TDERR_BadUnitNum Wrong unit number.
- TDERR_BadDriveType Strange drive type.
- TDERR_DriveInUse Someone is already using the drive.
- TDERR_PostReset Tea time, the system is resetted.
-
- There exist also four general device errors which all devices
- may use: (Defined in header file "exec/errors.h".)
-
- IOERR_OPENFAIL Could not open the device.
- IOERR_ABORTED Request aborted.
- IOERR_NOCMD Not a valid command.
- IOERR_BADLENGTH Bad length or value.
- -------------------------------------------------------------
-
-
-
- 6.6 EXAMPLES
-
- Example 1
- This program will use the Trackdisk Device to turn on and off
- the internal disk drive's motor.
-
- Example 2
- This program demonstrates how you can check what went wrong
- while you were using the Trackdisk Device. This example will
- try to use drive DF3:, which most of us does not have, and
- thus we will receive an error message. (Well if you have four
- disk drives connected to your Amiga there will not be any
- error message.)
-
- Example 3
- This example demonstrates how you can read data with help of
- the Trackdisk Device. You give this program four arguments
- (drive, head, cylinder and sector), and it will print out all
- data in that sector. You only have to expand this program a
- little and you will end up with a nice disk viewer.
-
- Example3 drive (0-3) head (0-1) cylinder (0-79) sector (0-10)
-
- Example 4
- This example contains a lot of small and useful functions
- that does almost everything you ever would like to do with
- the trackdisk device. The example has been written so you can
- easily use the functions in your own programs.
-
-
-